﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Dynamic;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Core;

namespace zijian666.Converts.Dynamic;

/// <summary>
/// 基于 <seealso cref="DataRow" /> 的动态类型
/// </summary>
internal class DynamicDataRow : DynamicObjectBase<DataRow>
{
    /// <summary>
    /// 初始化
    /// </summary>
    public DynamicDataRow(DataRow row, IConvertSettings convertSettings)
        : base(row, convertSettings)
    {
        if (row is null)
        {
            throw new ArgumentNullException(nameof(row));
        }
    }

    /// <summary>
    /// 初始化
    /// </summary>
    public DynamicDataRow(DataRowView row, IConvertSettings settings) : this(row.Row, settings)
    {
        if (row is null)
        {
            throw new ArgumentNullException(nameof(row));
        }
    }

    /// <inheritdoc />
    public override IEnumerable<string> GetDynamicMemberNames()
    {
        foreach (DataColumn col in Value!.Table.Columns)
        {
            yield return col.ColumnName;
        }
    }

    /// <inheritdoc />
    public override bool TryConvert(ConvertBinder binder, out object? result)
    {
        if (typeof(IConvertible).IsAssignableFrom(binder.ReturnType))
        {
            var arr = Value!.ItemArray;
            if ((arr.Length == 1) && TryChangeType(arr[0], binder.ReturnType, out result))
            {
                return true;
            }
        }
        return TryChangeType(Value, binder.ReturnType, out result);
    }

    /// <inheritdoc />
    public override bool TryGetMember(GetMemberBinder binder, out object? result)
    {
        result = Value![binder.Name];
        if (result is not null)
        {
            if (TryChangeType(result, binder.ReturnType, out result))
            {
                result = CreateDynamic(result);
                return true;
            }
        }

        result = DynamicPrimitive.Null;
        return true;
    }

    /// <inheritdoc />
    public override bool TrySetMember(SetMemberBinder binder, object? value)
    {
        if (IsReadOnly)
        {
            return false;
            //throw new NotSupportedException("当前对象是只读的");
        }
        Value![binder.Name] = value.To<string>();
        return true;
    }

    /// <inheritdoc />
    private object? Indexer(object[] indexes)
    {
        if ((indexes is null) || (indexes.Length != 1))
        {
            return null;
        }
        var index = indexes[0];
        if (index is string name)
        {
            if (Value!.Table.Columns.Contains(name))
            {
                return Value![name];
            }
            return null;
        }
        var i = index.Convert<int>(ConvertSettings).GetValueOrDefalut(-1);
        if ((i < 0) || (i > Value!.ItemArray.Length))
        {
            return null;
        }
        return Value.ItemArray[i];
    }

    /// <inheritdoc />
    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object? result)
    {
        result = Indexer(indexes);
        if (result is not null)
        {
            if (TryChangeType(result, binder.ReturnType, out result))
            {
                result = CreateDynamic(result);
                return true;
            }
        }

        result = DynamicPrimitive.Null;
        return true;
    }

    /// <inheritdoc />
    public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object? value)
    {
        if (IsReadOnly)
        {
            return false;
        }

        if (indexes.Length != 1)
        {
            return false;
        }
        var index = indexes[0];
        if (index is string name)
        {
            if (Value!.Table.Columns.Contains(name))
            {
                var col = Value!.Table.Columns[name];
                if (TryChangeType(value, col.DataType, out value))
                {
                    Value![col] = value;
                }
            }
        }
        else
        {
            var i = index.To(-1);
            if ((i >= 0) && (i < Value!.ItemArray.Length))
            {
                var col = Value!.Table.Columns[i];
                if (TryChangeType(value, col.DataType, out value))
                {
                    Value![col] = value;
                }
            }
        }
        return false;
    }
}
